#include "document.h"

#include "xdl/symbol.h"
#include "xdl/symbolwriter.h"
#include "xdl/symbolreader.h"

#include <QFile>
#include <QUndoStack>
#include <QLoggingCategory>

// TODO: within namespace?
Q_DECLARE_LOGGING_CATEGORY(LedaSymbolDocumentLog)
Q_LOGGING_CATEGORY(LedaSymbolDocumentLog, "leda.sch.document")

namespace SymbolEditor
{

    Document::Document(QObject *parent) :
        IDocument(parent),
        m_itemIndex(1)
    {
        setModified(true);
    }

    bool Document::load(QString *errorString, const QString &fileName)
    {
        Reader reader;
        auto symbol = reader.read(fileName); // FIXME: leak/ownership
        if (symbol == nullptr)
        {
            *errorString = reader.errorString();
            return false;
        }

        m_symbolName = symbol->name;
        m_symbolLabel = symbol->description;
        m_itemIndex = 0;
        m_drawingItemMap.clear();
        for (auto item : symbol->drawingItems)
        {
            addItem(item);
        }

        return true;
    }

    const Item *Document::item(quint64 id) const
    {
        if (!m_drawingItemMap.contains(id))
        {
            return nullptr;
        }
        return m_drawingItemMap.value(id);
    }

    QList<quint64> Document::itemIdList() const
    {
        return m_drawingItemMap.keys();
    }

    quint64 Document::addItem(Item *item, quint64 itemId)
    {
        quint64 index = itemId;
        if (index == 0)
        {
            m_itemIndex++;
            index = m_itemIndex;
        }
        item->setId(index);
        m_drawingItemMap.insert(index, item);
        m_itemStack.append(index);
        emit itemAdded(index, item);
        setModified(true);
        return index;
    }

    quint64 Document::cloneItem(quint64 id, quint64 cloneId)
    {
        auto item = m_drawingItemMap.value(id);
        auto clone = item->clone();
        return addItem(clone, cloneId);
    }

    quint64 Document::addItem(ItemType type, quint64 id)
    {
        // TODO: use a map of proptotypes (configured as per user preference)
        // and return addItem(m_prototypes.value(type).clone(), id);
        switch (type)
        {
            case Rectangle:
                return addItem(new RectangleItem(), id);
            case CircularArc:
                return addItem(new CircularArcItem(), id);
            case Ellipse:
                return addItem(new EllipseItem(), id);
            case EllipticalArc:
                return addItem(new EllipticalArcItem(), id);
            case Polyline:
                return addItem(new PolylineItem(), id);
            case Polygon:
                return addItem(new PolygonItem(), id);
            case Label:
                return addItem(new LabelItem(), id);
            case Pin:
                return addItem(new PinItem(), id);
            case Group:
                return 0; // FIXME
            case Circle:
                return addItem(new CircleItem, id);
        }
        return 0;
    }

    void Document::removeItem(quint64 id)
    {
        if (!m_drawingItemMap.contains(id))
        {
            return;
        }

        auto item = m_drawingItemMap.value(id);
        m_drawingItemMap.remove(id);
        m_itemStack.removeOne(id);
        emit itemRemoved(id);
        setModified(true);
        delete item;
    }

    // FIXME: setModified, then emit? or emit aboutToChange()? Think Qt model/view framework
    // FIXME: use if (itemOrWarning(itemId) == nullptr) { return; }
    // FIXME: use if (!hasPropertyOrWarning(itemId, propertyId)) { return; }
    // FIXME: Check QVariant can be converted?
    // FIXME: quint64 vs int
    void Document::setItemProperty(quint64 itemId, PropertyId propertyId, const QVariant &value)
    {
        if (!m_drawingItemMap.contains(itemId))
        {
            return;
        }
        auto item = m_drawingItemMap.value(itemId);

        item->setProperty(propertyId, value);
        emit itemChanged(itemId, item);
        emit itemPropertyChanged(itemId, propertyId, value);
        setModified(true);
        return;
    }

    QVariant Document::itemProperty(quint64 itemId, PropertyId propertyId) const
    {
        if (!m_drawingItemMap.contains(itemId))
        {
            return QVariant();
        }
        auto item = m_drawingItemMap.value(itemId);
        return item->property(propertyId);
    }

    QString Document::friendlyItemPropertyName(quint64 itemId, PropertyId propertyId)
    {
        if (!m_drawingItemMap.contains(itemId))
        {
            return "Unkown item";
        }
        auto item = m_drawingItemMap.value(itemId);
        return item->friendlyPropertyName(propertyId);
    }

    QString Document::friendlyItemTypeName(quint64 itemId)
    {
        if (!m_drawingItemMap.contains(itemId))
        {
            return "Unkown item";
        }
        auto item = m_drawingItemMap.value(itemId);
        return item->friendlyTypeName();
    }

    QList<quint64> Document::itemStack() const
    {
        return m_itemStack;
    }

    void Document::stackItemBefore(quint64 itemId, quint64 referenceId)
    {
        auto from = m_itemStack.indexOf(itemId);
        auto to = m_itemStack.indexOf(referenceId);
        if (to < 0)
        {
            to = 0;
        }
        m_itemStack.move(from, to);
        emit itemStackReordered(itemId, referenceId);
    }

    bool Document::save(QString *errorString, const QString &fileName)
    {
        Writer writer;
        Symbol symbol;
        symbol.name = m_symbolName;
        symbol.description = m_symbolLabel;
        symbol.drawingItems = m_drawingItemMap.values();
        writer.setAutoFormating(true);
        if (!writer.write(fileName, &symbol))
        {
            *errorString = writer.errorString();
            return false;
        }
        setModified(false);
        return true;
    }

    void Document::render(QPainter *painter)
    {
        Q_UNUSED(painter);
    }

}
